/**
 * 
 */
package com.ditty.handler;

import java.awt.List;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;

import com.ditty.HttpConfiguration;
import com.ditty.bean.ResponseBody;
import com.ditty.controller.ControllerFactory;
import com.ditty.invoke.action.Action;
import com.ditty.invoke.action.ActionInvoker;
import com.ditty.invoke.handler.HandlerInvoker;
import com.ditty.kit.ArrayKit;
import com.ditty.kit.CharsetKit;
import com.ditty.kit.HttpKit;
import com.ditty.kit.StrKit;
import com.ditty.kit.ValueKit;
import com.ditty.router.Routers;
import com.ditty.serialize.ISerializeConvertor;
import com.ditty.serialize.SerializeConvertorFactory;
import com.ditty.wrap.HttpRequestWrapper;
import com.ditty.wrap.HttpResponseWrapper;

/**
 * @author dingnate
 *
 */
public class ActionHandler extends AbstractHttpHandler {

	@Override
	public void handle(HandlerInvoker invoker) {
		String[] urlArgs = { null };
		Action action = Routers.me().get(invoker.getTarget(), urlArgs);
		if (action == null) {
			invoker.getHttpResponseWrapper().setStatusCode(HttpKit.STATUS_400);
			return;
		}
		Object[] args = buildArgs(action, urlArgs, invoker.getHttpRequestWrapper());
		try {
			handleReturnValue(action.getMethod().getReturnType() == void.class, invoker.getHttpResponseWrapper(), new ActionInvoker(action,
					ControllerFactory.getController(action.getControllerClass()), args).inovke().getReturnValue());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private void handleReturnValue(boolean isVoid, HttpResponseWrapper httpResponseWrapper, Object returnValue) throws IOException {
		if (!isVoid) {
			httpResponseWrapper.write(SerializeConvertorFactory.getSerializeConvertor().serialize(new ResponseBody(returnValue)));
		}
	}

	@SuppressWarnings({ "unchecked" })
	private Object[] buildArgs(Action action, String[] urlArgs, HttpRequestWrapper httpRequestWrapper) {
		Class<?>[] argTypes = action.getMethod().getParameterTypes();
		Object[] objects = new Object[argTypes.length];
		if (urlArgs[0] != null) {
			String[] split = StrKit.split(urlArgs[0], HttpConfiguration.ARG_SEPARATER, argTypes.length);
			if (split.length != argTypes.length) {
				throw new RuntimeException(String.format("arg count is unmatch! expect %s get %s", argTypes.length, split.length));
			}
			for (int i = 0; i < argTypes.length; i++) {
				objects[i] = ValueKit.convert(split[i], argTypes[i]);
			}
		} else {
			ISerializeConvertor serializeConvertor = SerializeConvertorFactory.getSerializeConvertor();
			HashMap<String, Object> argMap = serializeConvertor.deserialize(httpRequestWrapper.content(), HashMap.class);
			if (argMap.size() != argTypes.length) {
				throw new RuntimeException(String.format("arg count is unmatch! expect %s get %s", argTypes.length, argMap.size()));
			}

			String[] argNames = action.getArgNames();
			for (Entry<String, Object> entry : argMap.entrySet()) {
				String argName = entry.getKey();
				String value = entry.getValue().toString();
				int argIndex;
				if (Character.isDigit(argName.charAt(0))) {
					argIndex = Integer.valueOf(argName);
				} else {
					argIndex = ArrayKit.indexOf(argNames, argName);
				}
				Class<?> componentType = argTypes[argIndex].getComponentType();
				if (argTypes[argIndex].isArray()) {
					objects[argIndex] = convertToArray(serializeConvertor, value, componentType).toArray(
							(Object[]) Array.newInstance(argTypes[argIndex].getComponentType(), 0));
				} else if (List.class.isAssignableFrom(argTypes[argIndex])) {
					objects[argIndex] = convertToList(serializeConvertor, value, componentType);
				} else if (Set.class.isAssignableFrom(argTypes[argIndex])) {
					objects[argIndex] = convertToSet(serializeConvertor, value, componentType);
				} else {
					objects[argIndex] = convertToObject(serializeConvertor, value, argTypes[argIndex]);
				}
			}
		}
		return objects;
	}

	/**
	 * @param serializeConvertor
	 * @param value
	 * @param class1
	 * @return
	 */
	private Object convertToObject(ISerializeConvertor serializeConvertor, String value, Class<?> argType) {
		return serializeConvertor.deserialize(value.getBytes(CharsetKit.DEFAULT_CHARSET), argType);
	}

	/**
	 * @param serializeConvertor
	 * @param value
	 * @param componentType
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private HashSet convertToSet(ISerializeConvertor serializeConvertor, String value, Class<?> componentType) {
		return new HashSet(serializeConvertor.deserializeArray(value.getBytes(CharsetKit.DEFAULT_CHARSET), componentType));
	}

	/**
	 * @param serializeConvertor
	 * @param value
	 * @param componentType
	 * @return
	 */
	private java.util.List<?> convertToList(ISerializeConvertor serializeConvertor, String value, Class<?> componentType) {
		return serializeConvertor.deserializeArray(value.getBytes(CharsetKit.DEFAULT_CHARSET), componentType);
	}

	/**
	 * @param serializeConvertor
	 * @param value
	 * @param componentType
	 * @return
	 */
	private java.util.List<?> convertToArray(ISerializeConvertor serializeConvertor, String value, Class<?> componentType) {
		return serializeConvertor.deserializeArray(value.getBytes(CharsetKit.DEFAULT_CHARSET), componentType);
	}
}
